package com.unfbx.chatgpt.entity.completions;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.unfbx.chatgpt.utils.TikTokensUtil;
import lombok.*;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.*;

/**
 * 描述： 问题类
 *
 * @author https:www.unfbx.com
 * 2023-02-11
 */
@Data
@Builder
@Slf4j
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
@AllArgsConstructor
public class Completion implements Serializable {

    //KEYPOINT 这是问答参数
    @NonNull
    @Builder.Default
    private String model = Model.DAVINCI_003.getName();
    /**
     * 问题描述
     * 提示词就是用这个来进行交互
     */
    @NonNull
    private String prompt;
    /**
     * UNKOWN
     * 完成输出后的后缀，用于格式化输出结果
     */
    private String suffix;

    /**
     * 最大支持4096
     */
    @JsonProperty("max_tokens")
    @Builder.Default
    private Integer maxTokens = 2048;
    /**
     * 使用什么取样温度，0到2之间。较高的值(如0.8)将使输出更加随机，而较低的值(如0.2)将使输出更加集中和确定。
     * <p>
     * We generally recommend altering this or but not both.top_p
     */
    @Builder.Default
    private double temperature = 0;

    /**
     * 使用温度采样的替代方法称为核心采样，其中模型考虑具有top_p概率质量的令牌的结果。因此，0.1 意味着只考虑包含前 10% 概率质量的代币。
     * 举例来说，如果将 "top_p" 设置为 0.1，那么模型在生成文本时只会考虑那些概率质量排在前 10% 的令牌，而忽略掉概率质量较低的令牌。
     * 通常来说，建议对 "top_p" 这个参数进行调整以控制生成文本的多样性和流畅性，但不要同时调整 "temperature" 参数，因为它们是相互影响的。
     * 我们通常建议更改此设置，但不要同时更改两者。temperature
     */
    @JsonProperty("top_p")
    @Builder.Default
    private Double topP = 1d;

    /**
     * 为每个提示生成的完成次数。
     */
    @Builder.Default
    private Integer n = 1;

    /**
     * 流式布局是否使用
     */
    @Builder.Default
    private boolean stream = false;
    /**
     *在 ChatGPT API 中，参数 "logprobs" 用于控制是否返回生成文本时每个生成令牌的对数概率。当设置了 "logprobs" 参数并指定一个非空值时，API 将返回生成文本时每个令牌的对数概率，这些对数概率可以用于进一步分析或后处理生成的文本。
     * 具体来说，如果将 "logprobs" 设置为非空值（通常是一个整数），则 API 在生成文本时会同时返回每个生成令牌的对数概率。这些对数概率可以帮助用户更好地理解模型生成文本的过程，以及每个令牌被模型选择的可能性。
     *在这个例子中，"logprobs" 参数的最大值为 5，这意味着可以请求返回生成文本时每个令牌的对数概率，最多返回 5 个对数概率值。
     *识别不确定性： 对数概率可以反映模型在选择每个令牌时的不确定性程度。较高的对数概率通常表示模型更加自信地选择了特定的令牌，而较低的对数概率则可能表示模型在选择令牌时存在较大的不确定性。检查模型行为： 分析对数概率可以帮助用户了解模型的行为模式，例如模型倾向于选择哪些令牌或者在特定情况下会做出怎样的决策。调试和优化： 对数概率可以用于调试和优化生成模型。通过检查每个令牌的对数概率，用户可以确定模型是否在预期的方向上进行生成，以及是否需要调整模型的参数或输入。
     *
     * 最大值：5
     */
    private Integer logprobs;

    /**
     *在 ChatGPT API 中，这段代码表示了一个配置项，即是否启用回显功能。如果 echo 被设置为 true，
     * 则 API 将返回用户输入的文本作为对话的一部分，否则将不返回。
     * 这样做可以让用户更好地理解模型的响应，因为他们可以看到模型是如何理解并响应其输入的。
     */
    @Builder.Default
    private boolean echo = false;

    /**
     *
     *在 ChatGPT API 中，stop 参数用于指定一个停用词列表。停用词是指在自然语言处理任务中通常被忽略的词语，因为它们通常不携带太多实际的语义信息。例如，在文本生成任务中，像 "the"、"and"、"is" 这样的词语经常被认为是停用词，因为它们在文本生成过程中往往不会提供太多有用的信息。
     *通过在 ChatGPT API 中设置 stop 参数，用户可以传递一个列表，其中包含他们希望在文本生成过程中被过滤掉的停用词。这样，生成的文本就会更加准确和清晰，因为它们不再包含这些通常被视为无关紧要的词语。
     */
    private List<String> stop;

    /**
     *在 ChatGPT API 中，presence_penalty 参数用于控制生成文本中重复内容的惩罚程度。
     * 具体来说，它影响模型生成时对于已经出现过的文本片段的惩罚力度。
     * 当 presence_penalty 的值越大时，模型会更倾向于生成与已经生成过的文本不同的内容，
     * 从而减少重复性。相反，当 presence_penalty 的值较小或为零时，模型可能会更频繁地生成类似或重复的文本片段。
     * 通过调整 presence_penalty 参数，用户可以在生成文本时控制模型对于重复内容的处理方式。
     */
    @JsonProperty("presence_penalty")
    @Builder.Default
    private double presencePenalty = 0;

    /**
     * 在 ChatGPT API 中，`frequency_penalty` 参数用于控制生成文本中词语的频率惩罚程度。
     * 具体来说，它影响模型生成时对于高频词语的惩罚程度。当 `frequency_penalty` 的值越大时，
     * 模型会更倾向于避免使用高频词语，从而生成更加多样化的文本。相反，当 `frequency_penalty` 的值较小或为零时，
     * 模型可能会更频繁地使用高频词语，导致生成的文本中重复内容较多。
     * 通过调整 `frequency_penalty` 参数，用户可以在生成文本时控制模型对于高频词语的使用频率，从而影响生成文本的多样性。
     * -2.0 ~~ 2.0
     */
    @JsonProperty("frequency_penalty")
    @Builder.Default
    private double frequencyPenalty = 0;

    /**
     *
     *在 ChatGPT API 中，best_of 参数用于指定生成多个文本候选项后，选择其中最佳候选项的数量。
     * 具体来说，当设置 best_of 参数时，模型将生成多个文本，并从中选择 best_of 数量的最佳候选项。
     * 这些候选项通常是根据模型生成的文本质量进行评估和排序的。
     * 选择最佳候选项可以帮助用户获取更高质量、更符合预期的生成文本。
     * 默认情况下，best_of 参数设置为 1，表示仅选择单个最佳候选项。用户可以根据需要调整 best_of 参数的值，以获得满足其需求的最佳生成文本。
     */
    @JsonProperty("best_of")
    @Builder.Default
    private Integer bestOf = 1;

    /**
     *在 ChatGPT API 中，logit_bias 参数用于设置生成文本时每个词汇的偏置值。
     * 偏置值影响了模型在生成文本时选择特定词汇的倾向性。
     * 通过设置 logit_bias 参数，用户可以引导模型生成更加符合其预期或特定主题的文本。
     * 这个参数接受一个 Map 类型的输入，其中键是词汇，值是该词汇的偏置值。
     */
    @JsonProperty("logit_bias")
    private Map logitBias;





    /**
     * @since 1.1.2
     *在 ChatGPT API 中，seed 参数用于设置生成文本时的随机种子。随机种子影响模型在生成文本时的随机性，相同的种子将导致模型在相同条件下生成相同的文本。这对于需要在不同时间点获得相同结果的场景很有用，例如在调试、复现结果或进行可重复的实验时。
     */
    private Integer seed;

    /**
     *
     * 用户唯一值，确保接口不被重复调用
     *在 ChatGPT API 中，您可以通过将 user 参数设置为用户的唯一标识符或用户名来传递用户信息。这可以通过在 API 请求中包含 user 参数及其对应的值来完成。具体的传递方式取决于您使用的 API 客户端库或工具。通常，您可以在 API 请求的参数中指定 user 参数，就像指定其他参数一样。例如，在使用 HTTP 请求时，您可以将 user 参数作为 URL 查询参数传递；在使用 API 客户端库时，您可以将 user 参数作为函数或方法的参数传递。
     *通常情况下，user 参数用于区分不同用户发来的请求。在 ChatGPT API 中，用户可以通过设置 user 参数来标识自己。这对于记录和跟踪不同用户的请求以及对其进行个性化处理非常有用。例如，可以根据用户的历史记录或偏好来调整生成的文本内容。
     */
    private String user;

    /**
     * 获取当前参数的tokens数
     *
     * @return token数量
     */
    public long tokens() {
        if (StrUtil.isBlank(this.prompt) || StrUtil.isBlank(this.model)) {
            log.warn("参数异常model：{}，prompt：{}", this.model, this.prompt);
            return 0;
        }
        return TikTokensUtil.tokens(this.model, this.prompt);
    }

    /**
     * 模型以枚举的形式列出，可以添加，上面可以更改默认模型
     */
    @Getter
    @AllArgsConstructor
    public enum Model {
        DAVINCI_003("text-davinci-003"),
        DAVINCI_002("text-davinci-002"),
        DAVINCI("davinci"),
        ;
        private final String name;
    }
}
